package com.alipay.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;





import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.methods.multipart.FilePartSource;
import org.apache.commons.httpclient.methods.multipart.PartSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.alipay.config.AlipayConfig;
import com.weixin.common.util.StringUtils;

/* *
 *类名：AlipayFunction
 *功能：支付宝接口公用函数类
 *详细：该类是请求、通知返回两个文件所调用的公用函数核心处理文件，不需要修改
 *版本：3.3
 *日期：2012-08-14
 *说明：
 *以下代码只是为了方便商户测试而提供的样例代码，商户可以根据自己网站的需要，按照技术文档编写,并非一定要使用该代码。
 *该代码仅供学习和研究支付宝接口使用，只是提供一个参考。
 */

public class AlipayCore {
	
	private final static Logger LOG = LoggerFactory.getLogger(AlipayCore.class);

	/** 
	 * 除去数组中的空值和签名参数
	 * @param sArray 签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {

		Map<String, String> result = new HashMap<String, String>();

		if (sArray == null || sArray.size() <= 0) {
			return result;
		}

		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
					|| key.equalsIgnoreCase("sign_type")) {
				continue;
			}
			result.put(key, value);
		}

		return result;
	}

	/** 
	 * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
	 * @param params 需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
				prestr = prestr + key + "=" + value;
			} else {
				prestr = prestr + key + "=" + value + "&";
			}
		}

		return prestr;
	}

	/** 
	 * 写日志，方便测试（看网站需求，也可以改成把记录存入数据库）
	 * @param sWord 要写入日志里的文本内容
	 */
	public static void logResult(String sWord) {
		FileWriter writer = null;
		try {
			writer = new FileWriter(AlipayConfig.log_path + "alipay_log_" + System.currentTimeMillis()+".txt");
			writer.write(sWord);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/** 
	 * 生成文件摘要
	 * @param strFilePath 文件路径
	 * @param file_digest_type 摘要算法
	 * @return 文件摘要结果
	 */
	public static String getAbstract(String strFilePath, String file_digest_type) throws IOException {
		PartSource file = new FilePartSource(new File(strFilePath));
		if(file_digest_type.equals("MD5")){
			return DigestUtils.md5Hex(file.createInputStream());
		}
		else if(file_digest_type.equals("SHA")) {
			return DigestUtils.sha256Hex(file.createInputStream());
		}
		else {
			return "";
		}
	}

	/**
	 * 当面付退款，不适用于快捷支付如app支付
	 * 参数	类型	是否必填	最大长度	描述	示例值
	out_trade_no	String	特殊可选	64	订单支付时传入的商户订单号,不能和 trade_no同时为空。	20150320010101001
	trade_no	String	特殊可选	64	支付宝交易号，和商户订单号不能同时为空	2014112611001004680 073956707
	refund_amount	Price	必须	9	需要退款的金额，该金额不能大于订单金额,单位为元，支持两位小数	200.12
	refund_reason	String	可选	256	退款的原因说明	正常退款
	out_request_no	String	可选	64	标识一次退款请求，同一笔交易多次退款需要保证唯一	HZ01RF001
	operator_id	String	可选	30	商户的操作员编号	OP001
	store_id	String	可选	32	商户的门店编号	NJ_S_001
	terminal_id	String	可选	32	商户的终端编号	NJ_T_001
	响应参数
	参数	类型	是否必填	最大长度	描述	示例值
	trade_no	String	必填	64	2013112011001004330000121536	支付宝交易号
	out_trade_no	String	必填	64	商户订单号	6823789339978248
	open_id	String	必填	32	买家支付宝用户号，该参数已废弃，请不要使用	2088102122524333
	buyer_logon_id	String	必填	100	用户的登录id	159****5620
	fund_change	String	必填	1	本次退款是否发生了资金变化 	Y
	refund_fee	Price	必填	11	本次发生的退款金额	88.88
	gmt_refund_pay	Date	必填	32	退款支付时间	2014-11-27 15:45:57
	refund_detail_item_list	TradeFundBill []	选填		退款返回的资金明细类型	
	store_name	String	选填	512	交易在支付时候的门店名称	望湘园联洋店
	buyer_user_id	String	必填	28	买家在支付宝的用户id	2088101117955611
	send_back_fee	String	选填	11	实际退回给用户的金额	1.8
	 * @throws AlipayApiException 
	 */
	public static boolean refund(String out_trade_no, String trade_no,
			float refund_amount, String refund_reason, String out_request_no,
			String operator_id, String store_id, String terminal_id)
			throws AlipayApiException
	{
		AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
															 AlipayConfig.app_id,
															 AlipayConfig.private_key,
															 "json",
															 "UTF-8",
															 AlipayConfig.ali_public_key);
		AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();

		Map<String, Object> map=new HashMap<String, Object>();
		map.put("out_trade_no",out_trade_no);
		map.put("trade_no",trade_no);
		map.put("refund_amount",refund_amount);
		map.put("refund_reason",refund_reason);
		map.put("out_request_no",out_request_no);
		map.put("operator_id",operator_id);
		map.put("store_id",store_id);
		map.put("terminal_id",terminal_id);

		JSONObject jsonObject=new JSONObject(map);
		String bizContent=jsonObject.toJSONString();
		request.setBizContent(bizContent);
		LOG.error("refundRequest="+JSON.toJSONString(request));
		AlipayTradeRefundResponse response = alipayClient.execute(request);
		if (!response.isSuccess()) {
			System.out.println(response.getMsg());
			System.out.println(response.getSubMsg());

		}
		return response.isSuccess();
	}
	
	/*
	 * 快捷支付退款接口,需要用户输入支付宝密码
	 * 不能直接接口调用，只能生成表单数据提交，返回的也是html内容。
	 * service	接口名称	String	接口名称。	不可空	refund_fastpay_by_platform_nopwd
	partner	合作者身份ID	String(16)	签约的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。	不可空	2088101008267254
	_input_charset	参数编码字符集	String	商户网站使用的编码格式，如utf-8、gbk、gb2312等。	不可空	GBK
	sign_type	签名方式	String	DSA、RSA、MD5三个值可选，必须大写。	不可空	MD5
	sign	签名	String	请参见签名。	不可空	tphoyf4aoio5e6zxoaydjevem2c1s1zo
	notify_url	服务器异步通知页面路径	String(200)	支付宝服务器主动通知商户网站里指定的页面http路径。	可空	http://api.test.alipay.net/atinterface/receive_notify.htm
	seller_email	卖家支付宝账号	String	如果卖家Id已填，则此字段可为空。	不可空	Jier1105@alitest.com
	seller_user_id	卖家用户ID	String	卖家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字。登录时，seller_email和seller_user_id两者必填一个。如果两者都填，以seller_user_id为准。	不可空	2088101008267254
	refund_date	退款请求时间	String	退款请求的当前时间。格式为：yyyy-MM-dd hh:mm:ss。	不可空	2011-01-12 11:21:00
	batch_no	退款批次号	String	每进行一次即时到账批量退款，都需要提供一个批次号，通过该批次号可以查询这一批次的退款交易记录，对于每一个合作伙伴，传递的每一个批次号都必须保证唯一性。格式为：退款日期（8位）+流水号（3～24位）。不可重复，且退款日期必须是当天日期。流水号可以接受数字或英文字符，建议使用数字，但不可接受“000”。	不可空	201101120001
	batch_num	总笔数	String	即参数detail_data的值中，“#”字符出现的数量加1，最大支持1000笔（即“#”字符出现的最大数量为999个）。	不可空	1
	detail_data	单笔数据集	String	退款请求的明细数据。格式详情参见下面的“单笔数据集参数说明”。	不可空	2011011201037066^5.00^协商退款
	 */
	public static String nopwdRefund(String orderNo,String trade_no,float refund_amount,String refund_reason) 
	{    	
		////////////////////////////////////请求参数//////////////////////////////////////
		//批次号，必填，格式：当天日期[8位]+序列号[3至24位]，如：201603081000001
		String batch_no = getCurrrentDate("yyyyMMdd")+orderNo;

		//退款笔数，必填，参数detail_data的值中，“#”字符出现的数量加1，最大支持1000笔（即“#”字符出现的数量999个）

		String batch_num = "1";

		//退款详细数据，必填，格式（支付宝交易号^退款金额^备注），多笔请用#隔开
		String detail_data = trade_no+"^"+refund_amount+"^"+refund_reason;

		//把请求参数打包成数组
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("service", AlipayConfig.refund_nopwd_service);
		sParaTemp.put("partner", AlipayConfig.partner_id);
		sParaTemp.put("_input_charset", "UTF-8");
		sParaTemp.put("notify_url", AlipayConfig.notify_url);
		sParaTemp.put("seller_user_id", AlipayConfig.seller_user_id);
		sParaTemp.put("refund_date", AlipayConfig.refund_date);
		sParaTemp.put("batch_no", batch_no);
		sParaTemp.put("batch_num", batch_num);
		sParaTemp.put("detail_data", detail_data);

		//除去数组中的空值和签名参数
		Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
		//生成签名结果
		String mysign = AlipaySubmit.buildRequestMysign(sPara);

		//签名结果与签名方式加入请求提交参数组中
		sPara.put("sign", mysign);
		sPara.put("sign_type", AlipayConfig.sign_type);
 
 
		//建立请求
		String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");

		return sHtmlText;
	}

	/*
	 * 快捷支付退款接口,需要用户输入支付宝密码
	 * 不能直接接口调用，只能生成表单数据提交，返回的也是html内容。
	 * service	接口名称	String	接口名称。	不可空	refund_fastpay_by_platform_pwd
	partner	合作者身份ID	String(16)	签约的支付宝账号对应的支付宝唯一用户号。以2088开头的16位纯数字组成。	不可空	2088101008267254
	_input_charset	参数编码字符集	String	商户网站使用的编码格式，如utf-8、gbk、gb2312等。	不可空	GBK
	sign_type	签名方式	String	DSA、RSA、MD5三个值可选，必须大写。	不可空	MD5
	sign	签名	String	请参见签名。	不可空	tphoyf4aoio5e6zxoaydjevem2c1s1zo
	notify_url	服务器异步通知页面路径	String(200)	支付宝服务器主动通知商户网站里指定的页面http路径。	可空	http://api.test.alipay.net/atinterface/receive_notify.htm
	seller_email	卖家支付宝账号	String	如果卖家Id已填，则此字段可为空。	不可空	Jier1105@alitest.com
	seller_user_id	卖家用户ID	String	卖家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字。登录时，seller_email和seller_user_id两者必填一个。如果两者都填，以seller_user_id为准。	不可空	2088101008267254
	refund_date	退款请求时间	String	退款请求的当前时间。格式为：yyyy-MM-dd hh:mm:ss。	不可空	2011-01-12 11:21:00
	batch_no	退款批次号	String	每进行一次即时到账批量退款，都需要提供一个批次号，通过该批次号可以查询这一批次的退款交易记录，对于每一个合作伙伴，传递的每一个批次号都必须保证唯一性。格式为：退款日期（8位）+流水号（3～24位）。不可重复，且退款日期必须是当天日期。流水号可以接受数字或英文字符，建议使用数字，但不可接受“000”。	不可空	201101120001
	batch_num	总笔数	String	即参数detail_data的值中，“#”字符出现的数量加1，最大支持1000笔（即“#”字符出现的最大数量为999个）。	不可空	1
	detail_data	单笔数据集	String	退款请求的明细数据。格式详情参见下面的“单笔数据集参数说明”。	不可空	2011011201037066^5.00^协商退款
	 */
	public static String pwdRefund(String orderNo,String trade_no,float refund_amount,String refund_reason) 
	{    	
		////////////////////////////////////请求参数//////////////////////////////////////
		//批次号，必填，格式：当天日期[8位]+序列号[3至24位]，如：201603081000001
		String batch_no = getCurrrentDate("yyyyMMdd")+orderNo;

		//退款笔数，必填，参数detail_data的值中，“#”字符出现的数量加1，最大支持1000笔（即“#”字符出现的数量999个）

		String batch_num = "1";

		//退款详细数据，必填，格式（支付宝交易号^退款金额^备注），多笔请用#隔开
		String detail_data = trade_no+"^"+refund_amount+"^"+refund_reason;

		//把请求参数打包成数组
		Map<String, String> sParaTemp = new HashMap<String, String>();
		sParaTemp.put("service", AlipayConfig.refund_pwd_service);
		sParaTemp.put("partner", AlipayConfig.partner_id);
		sParaTemp.put("_input_charset", "UTF-8");
		//		sParaTemp.put("sign_type", AlipayConfig.sign_type);
		sParaTemp.put("notify_url", AlipayConfig.notify_url);
		//sParaTemp.put("seller_email", AlipayConfig.seller_email);
		sParaTemp.put("seller_user_id", AlipayConfig.seller_user_id);
		sParaTemp.put("refund_date", AlipayConfig.refund_date);
		sParaTemp.put("batch_no", batch_no);
		sParaTemp.put("batch_num", batch_num);
		sParaTemp.put("detail_data", detail_data);

		//除去数组中的空值和签名参数
		Map<String, String> sPara = AlipayCore.paraFilter(sParaTemp);
		//生成签名结果
		String mysign = AlipaySubmit.buildRequestMysign(sPara);

		//签名结果与签名方式加入请求提交参数组中
		sPara.put("sign", mysign);
		sPara.put("sign_type", AlipayConfig.sign_type);
 
 
		//建立请求
		String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,"get","确认");

		return sHtmlText;
	}
	
	/**
	 * 获取当前时间的字符串
	 * @param format 默认转化格式 yyyy-MM-dd HH:mm:ss
	 * @return String
	 */
	public static String getCurrrentDate(String format){
		if(StringUtils.isEmpty(format)){
			format = "yyyy-MM-dd HH:mm:ss";
		}
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		Date date = new Date();
		
		return sdf.format(date);
	}
}
